
/*
PARI/GP scripts: Vector utilities
=============================================================
File: VectorUtilities.txt    
Link: https://oeis.org/wiki/File:VectorUtilities.txt
=============================================================
*/


/* ============================= Testing vector properties */

OccursInVector(v,e,countall=0) =
/* ----------------------------------------------------------
Test whether v contains an element e. Return 0 when not.
Otherwise, when countall is 0, returns 1, or, when countall
is nonzero, return the number of occurrences of e. 
---------------------------------------------------------- */
{
  my(count=0);
  for (k=1,#v,
    if (v[k]==e,count++; if(!countall,break));
  );
  return(count);
}


IsIncreasingVector(v,strict=0) =
/* ----------------------------------------------------------
  Returns 1  if the entries in vector v are increasing
  (when strict==1) or non-decreasing (when strict==0).
  Otherwise, returns 0. When #v<2, returns 1. 
---------------------------------------------------------- */
{
  my (k);
  if (#v < 2, return (1)); 
  for (k=2,#v,
    if (v[k] < v[k-1], return (0));
    if (strict && (v[k] == v[k-1]), return (0));
  );
  return (1);
}

IsDecreasingVector(v,strict=0) =
/* ----------------------------------------------------------
  Returns 1 if the entries in vector v are decreasing
  (when strict==1) or non-increasing (when strict==0).
  Otherwise, returns 0. When #v<2, returns 1.
---------------------------------------------------------- */
{
  my (k);
  if (#v < 2, return (1)); 
  for (k=2,#v,
    if (v[k] > v[k-1], return (0));
    if (strict && (v[k] == v[k-1]), return (0));
  );
  return (1);
}

/* ==================================== Vector comparisons */

VectorsDifferAt(v1,v2) =
/* ----------------------------------------------------------
  Tests whether the two integer vectors are identical
  up to the length of the shorter one. Returns the first
  index at which inequality occurs, or 0 if it never happens.
---------------------------------------------------------- */
{
  my(n=#v1);if(#v2<n,n=#v2);
  for (k=1,n,if(v1[k]!=v2[k],return (k)));
  return (0);
}

VectorsMatchAt(v1,v2) =
/* ----------------------------------------------------------
  Tests whether the two integer vectors differ up to the
  length of the shorter one. Returns the first index at
  which a match occurs, or 0 if it never happens.
---------------------------------------------------------- */
{
  my(n=#v1);if(#v2<n,n=#v2);
  for (k=1,n,if(v1[k]==v2[k],return (k)));
  return (0);
}

VectorLtVectorAt(v1,v2) =
/* ----------------------------------------------------------
  Tests for all n up to the length of the shorter vector.
  Returns the first index at which v1[n] < v2[n],
  or 0 if it never happens.
---------------------------------------------------------- */
{
  my(n=#v1);if(#v2<n,n=#v2);
  for (k=1,n,if(v1[k]<v2[k],return (k)));
  return (0);
}

VectorLeVectorAt(v1,v2) =
/* ----------------------------------------------------------
  Tests for all n up to the length of the shorter vector.
  Returns the first index at which v1[n] <= v2[n],
  or 0 if it never happens.
---------------------------------------------------------- */
{
  my(n=#v1);if(#v2<n,n=#v2);
  for (k=1,n,if (v1[k]<=v2[k],return (k)));
  return (0);
}

VectorGtVectorAt(v1,v2) =
/* ----------------------------------------------------------
  Tests for all n up to the length of the shorter vector.
  Returns the first index at which v1[n] > v2[n],
  or 0 if it never happens.
---------------------------------------------------------- */
{
  my(n=#v1);if(#v2<n,n=#v2);
  for (k=1,n,if (v1[k]>v2[k],return (k)));
  return (0);
}

VectorGeVectorAt(v1,v2) =
/* ----------------------------------------------------------
  Tests for all n up to the length of the shorter vector.
  Returns the first index at which v1[n] >= v2[n],
  or 0 if it never happens.
---------------------------------------------------------- */
{
  my(n=#v1);if(#v2<n,n=#v2);
  for (k=1,n,if (v1[k]>=v2[k],return (k)));
  return (0);
}

VectorLtExprAt(v,expr) =
/* ----------------------------------------------------------
  Tests v[n] against the expression expr(n). The expression
  must be of the type expr(n) and its return value must be
  of the same type as v[n]. 
  Returns the first index at which v[n] < expr(n),
  or 0 if it never happens.
---------------------------------------------------------- */
{
  for(k=1,#v,if(v[k]<expr(k),return (k)));
  return (0);
}

VectorLeExprAt(v,expr) =
/* ----------------------------------------------------------
  Tests v[n] against the expression expr(n). The expression
  must be of the type expr(n) and its return value must be
  of the same type as v[n]. 
  Returns the first index at which v[n] <= expr(n),
  or 0 if it never happens.
---------------------------------------------------------- */
{
  for(k=1,#v,if(v[k]<=expr(k),return (k)));
  return (0);
}

VectorGtExprAt(v,expr) =
/* ----------------------------------------------------------
  Tests v[n] against the expression expr(n). The expression
  must be of the type expr(n) and its return value must be
  of the same type as v[n]. 
  Returns the first index at which v[n] > expr(n),
  or 0 if it never happens.
---------------------------------------------------------- */
{
  for(k=1,#v,if(v[k]>expr(k),return (k)));
  return (0);
}

VectorGeExprAt(v,expr) =
/* ----------------------------------------------------------
  Tests v[n] against the expression expr(n). The expression
  must be of the type expr(n) and its return value must be
  of the same type as v[n]. 
  Returns the first index at which v[n] >= expr(n),
  or 0 if it never happens.
---------------------------------------------------------- */
{
  for(k=1,#v,if(v[k]>=expr(k),return (k)));
  return (0);
}

/* ========================== Simple operations on vectors */
/* Note: These are not in-place operations.
         The returned vector is a new one.
*/

ReverseVector(v) =
/* ----------------------------------------------------------
  Reverses the order of elements in v, so that
  v[#v] is swapped with v[1], v[#v-1] with v[2], etc.
  To reverse a portion of a vector between (and including)
  indices i,j, use ReverseVector(v[i..j]).
---------------------------------------------------------- */
{
  my (n=1+#v,aux);
  for (k=1,n,
    if (k>=n-k, break, aux=v[k];v[k]=v[n-k];v[n-k]=aux);
  );
  return (v);
}

RunningSum(v) =
/* ----------------------------------------------------------
  Replaces v by its running sum.   
---------------------------------------------------------- */
{
  for (k=2,#v, v[k]+=v[k-1]);
  return (v);
}

RunningSumInverse(v) =
/* ----------------------------------------------------------
  Inverse of RunningSum(v).
---------------------------------------------------------- */
{
  my (n=#v);
  for (k=1,n-1, v[n-k+1]-=v[n-k]);
  return (v);
}

RunningProduct(v) =
/* ----------------------------------------------------------
  Replaces v by its running product.
---------------------------------------------------------- */
{
  for (k=2,#v, v[k]*=v[k-1]);
  return (v);
}

RunningProductInverse(v) =
/* ----------------------------------------------------------
  Inverse of RunningProduct(v)
  Attention: no test for division by 0 is done.
---------------------------------------------------------- */
{
  my (n=#v);
  for (k=1,n-1, v[n-k+1]/=v[n-k]);
  return (v);
}


/* ==================================== Vectors generation */


/* ----------------------------------------------------------
  Note: Functions Numerators(rvec) and Denumerators(rvec)
  present in the previous version were eliminated since
  they were redundant. The intrinsic PARI/GP functions
  numerator(rvec) and denumerator(rvec) do the same job
  also for rational-valued vector arguments.
---------------------------------------------------------- */

ListNCond0(nmax,condition,anmax=0) =
/* ----------------------------------------------------------
  Generates a list of natural numbers satisfying a condition.
  The argument nmax is the desired size of the vector.
  When anmax>0 is set, however, the list is truncated once
  the value to be tested exceeds anmax.
  The condition function form must be: BOOL condition(n).
  Example: list numbers n such that n is coprime to n\10:
    > VecNCont0(10000,IsCoprimeToRTrunc10)  
---------------------------------------------------------- */
{
  my (n=0,i=0,v=vector(nmax));
  while(1,i++;if((anmax>0)&&(i>anmax),break);
    if(condition(i),n++;v[n]=i;if(n==nmax,break)));
  return(v[1..n]);
}

ListVFCond0(cond,file,fmonit,monit=1000,nini=1,idxini=1,nmax=0,idxmax=0) =
/* ----------------------------------------------------------
  Similar to the above, but save results in a file. The
  testing starts with n=nini and then n keeps incrementing.
  For each n, the condition cond(n) is evaluated.

  When an n satisfies the condition, it is written into the
  'file' and also printed. When monit>0, and the just tested
  n is its multiple, it is written into the file 'fmonit',
  together with the number of matching n values so far found.
  The output into the 'fmonit' file is also printed, making
  it easy to follow the progress.

  When nmax>0 is set, the listing is interrupted once the 
  the value n to be tested exceeds it.
---------------------------------------------------------- */
{
  my (n=nini,idx=idxini);
  while(1,
    if(cond(n),print(idx," ",n);write(file,idx," ",n);
       idx++;if((idxmax>0)&&(idx>=idxmax),break));
    if(monit && (n%monit==0),
       print(n,", ",idx);write(fmonit,n,", ",idx));
    if((nmax>0)&&(n>=nmax),break);
    n++);
}

DensityNCond0(nmax,condition,monit=0) =
/* ----------------------------------------------------------
Estimates the density of naturals satisfying a test condition.
The condition function must be of the form: BOOL cond(n).
The argument nmax is the desired maximum value of n to test.
Returns the ratio k/nmax, where k is the number n-values not
exceeding nmax which satisfy the condition. 
Example: density of numbers n such that n is coprime to n\10:
  >  DensityNCond1(10^7,IsCoprimeToRTrunc10).  
---------------------------------------------------------- */
{
  my (k=0,n=0);
  while(1,n++;if(condition(n,m),k++;
    if(monit && (k%1000==0),print(n," ",k," ",0.0+k/n));
    if(n==nmax,break)));
  return(k/n);
}

ListNCond1(nmax,condition,p,anmax=0) =
/* ----------------------------------------------------------
  Generates a list of natural numbers satisfying a condition.
  The argument nmax is the desired size of the vector.
  When anmax>0 is set, however, the list is truncated once
  the value to be tested exceeds anmax.
  The cond function must be of the form
    BOOL condition(t_INT n,t_INT p), where p is a parameter.
  Example:
    List numbers n<10^9 such that n is coprime to n\16:
    ListNCont1(10000,IsCoprimeToRTrunc,16,1000000000)  
---------------------------------------------------------- */
{
  my (n=0,i=0,v=vector(nmax));
  while(1,i++;if((anmax>0)&&(i>anmax),break);
    if(condition(i,p),n++;v[n]=i;if(n==nmax,break)));
  return(v[1..n]);
}

ListVFCond1(cond,p,file,fmonit,monit=1000,nini=1,idxini=1,nmax=0,idxmax=0) =
/* ----------------------------------------------------------
  Similar to the above, but writes results into a file. The
  testing starts with n=nini and then n keeps incrementing.
  For each n, the condition cond(n,p) is evaluated.

  When an n satisfies the condition, it is written into the
  'file' and also printed. When monit>0, and the just tested
  n is its multiple, it is written into the file 'fmonit',
  together with the number of matching n values so far found.
  The output into the 'fmonit' file is also printed, making
  it easy to follow the progress.

  When nmax>0 is set, the listing is interrupted once the 
  the value n to be tested exceeds it.
---------------------------------------------------------- */
{
  my (n=nini,idx=idxini);
  while(1,
    if(cond(n,p),print(idx," ",n);write(file,idx," ",n);
       idx++;if((idxmax>0)&&(idx>=idxmax),break));
    if(monit && (n%monit==0),
       print(n,", ",idx);write(fmonit,n,", ",idx));
    if((nmax>0)&&(n>=nmax),break);
    n++);
}

DensityNCond1(nmax,condition,m,monit=0) =
/* ----------------------------------------------------------
Estimates the density of naturals satisfying a test condition.
The condition function must be of the form
  BOOL cond(t_INT n,t_INT m), where m is a parameter.
The argument nmax is the desired maximum value of n to test.
Returns the ratio k/nmax, where k is the number n-values not
exceeding nmax which satisfy the condition. 
Example: density of numbers n such that n is coprime to n\16:
  >  DensityNCond1(10^7,IsCoprimeToRTrunc,16).  
---------------------------------------------------------- */
{
  my (k=0,n=0);
  while(1,n++;if(condition(n,m),k++;
    if(monit && (k%1000==0),print(n," ",k," ",0.0+k/n));
    if(n==nmax,break)));
  return(k/n);
}

RecordVectorEntries(v,rexpr) =
/* ----------------------------------------------------------
Returns a vector of indexes of vector v at which rexpr(n,v[n])
breaks a record. The rexpr argument refers to a pre-defined
function rexpr(n,m) returning a t_REAL value.
In the simplest case  rexpr(n,m)=m (list of simple records).
Another popular case: rexpr(n,m)=m/n (record mean desities).
---------------------------------------------------------- */
{
  my(records=vector(#v),idx=1,e,f=rexpr(1,v[1]));
  records[idx] = 1;
  for(k=2,#v, e=rexpr(k,v[k]);
    if(e>f,f=e;idx++;records[idx]=k));
  return(records[1..idx]);
}


Egf2CoefSeq(func,nc=20,file="",c0=0.12345) =
/* ----------------------------------------------------------
Given an analytical function func(z) of real or complex
variable, generates the coefficients of the sequence of
which func is the e.g.f.
The resulting nc coefficients are printed and, if desired,
written into a plain text file.
In general, the coefficients are real.
No attempt is made to convert them into integers.
The routine works only with function for which Vec(func(x))
succeeds. For example, sign(x) or sqrt(x) cause an error.
Naturally, a function defined as func(x)=sqrt(x+a) will
work fine for any nonzero value of a (even a=1e-20, for
example) givint the coefficients of the expansion in terms
of (x-a)^n/n!. Try with func(x)=sqrt(x+1).
----------------
The following problem with PARI/GP was encountered:
Using the Vec(func(x),nc) function, the generated vector
skips the leading coefficients which are exactly 0 (for
example, x^6*func(x) would not show the two starting zeros).
To overcome this problem, Egf2CoefSeq computes coefficients
of func(x)+c0 and then subtracts c0 from the first one.
This hack would fail should it happen that func(0)=-c0,
but such cases are unlikely to happen, and easy to foresee
and to avoid, specifying any c0 value other than -func(0).

Example: with f(z)=atan(z)*exp(z),
Egf2CoefSeq(f,15,"myfile.txt") generates this output:
-------------------------------------------
E.g.f. coefficients of (z)->atan(z)*exp(z):

0	0.E-39
1	1
2	2
3	1
4	-4
5	9
6	110
7	-279
8	-4520
9	17265
10	322618
11	-1638031
12	-35226860
13	223578809
14	5463436134
---------------------------------------------------------- */
{
  my(n=nc,v);
  if(nc>default(seriesprecision),default(seriesprecision,nc));
  v=Vec(func(x)+c0,nc); v[1]-=c0; if(#v<n,n=#v);
  print("\nE.g.f. coefficients of ",func,":\n");
  if(file!="",write(file,"\nE.g.f. coefficients of ",func,":\n"));
  for(k=1,n,
    v[k]*=(k-1)!;
    print(k-1," \t",v[k]);
    if(file!="",write(file,k-1,"\t",v[k]));
  );
  return(v);
}

/*
=============================================================
   Contributed to OEIS Wiki by Stanislav Sykora
=============================================================
*/
